/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_CGSTATEMANAGER_H
#define M_CGSTATEMANAGER_H

#include "singleton.h"
#include "graphic.h"
#include "cgheaders.h"

#include <map>
#include <string>

namespace Engine
{
	struct CGPassData
	{
		Color ambient;
		Color diffuse;
		Color specular;
		Color emissive;
		REAL shininess;

		bool depth_check;
		bool depth_write;
		REAL depth_bias[2];
		CompareFunction depth_func;

		ShadeMode shading;
		FaceCullMode cull_mode;
		bool lighting;
		PolygonMode polygon_mode;

		CompareFunction alpha_rejection_func;
		unsigned char alpha_rejection_val;

		SceneBlendFactor scene_blend[2];

		FogMode fog_mode;
		Color   fog_color;
		REAL	fog_density;
		REAL    fog_start;
		REAL    fog_end;

		REAL point_size;
		bool point_sprites;
		bool point_size_attenuation_enabled;
		REAL point_size_attenuation[3];
		REAL point_size_min;
		REAL point_size_max;

		bool stencil;
		CompareFunction stencil_func;
		unsigned int stencil_ref_value;
		unsigned int stencil_mask;
		StencilOperation stencil_fail;
		StencilOperation stencil_depth_fail;
		StencilOperation stencil_pass;

		struct TextureData
		{
			std::string name;
			UVWAddressingMode address_mode;
			Color border_color;
			FilterOptions min_filter;
			FilterOptions mag_filter;
			FilterOptions mip_filter;
			unsigned int anisotropy;
			REAL bias;
			LayerBlendModeEx color_blend_mode;
			LayerBlendModeEx alpha_blend_mode;
			Matrix4<REAL> transform;
			TexCoordDim tex_transform_coord_dim;
			TexCoordCalcMethod tex_coord_calc;
			unsigned int tex_coord_calc_index;
		} texture[8];

		CGPassData(){Reset();}
		void Reset()
		{
			ambient = Color::White;
			diffuse = Color::White;
			specular = Color::White;
			emissive = Color::White;
			shininess = 0;

			depth_check = true;
			depth_write = true;
			depth_bias[0] = depth_bias[1] = 0;
			depth_func = CMPF_LESS_EQUAL;

			shading = SM_GOURAUD;
			cull_mode = FCM_CCW;
			lighting = true;
			polygon_mode = PM_SOLID;

			alpha_rejection_func = CMPF_ALWAYS_PASS;
			alpha_rejection_val = 0xff;

			scene_blend[0] = SBF_ONE;
			scene_blend[1] = SBF_ZERO;

			fog_mode = FOG_NONE;
			fog_color = Color::White;
			fog_density = 0;
			fog_start = 0;
			fog_end = 1000;

			point_size = 1;
			point_sprites = false;
			point_size_attenuation_enabled = false;
			point_size_attenuation[0] = 0;
			point_size_attenuation[1] = 1;
			point_size_attenuation[2] = 0;
			point_size_min = 1;
			point_size_max = 1;

			stencil = false;
			stencil_func = CMPF_ALWAYS_PASS;
			stencil_ref_value = 0;
			stencil_mask = 0xFFFFFFFF;
			stencil_fail = SOP_KEEP;
			stencil_depth_fail = SOP_KEEP;
			stencil_pass = SOP_KEEP;

			for(int i = 0; i < 8; ++i)
			{
				texture[i].name.clear();

				texture[i].address_mode.u = TAM_WRAP;
				texture[i].address_mode.v = TAM_WRAP;
				texture[i].address_mode.w = TAM_WRAP;

				texture[i].border_color = Color(0,0,0,0);

				texture[i].min_filter = FO_POINT;
				texture[i].mag_filter = FO_POINT;
				texture[i].mip_filter = FO_NONE;

				texture[i].anisotropy = 1;

				texture[i].bias = 0;

				texture[i].color_blend_mode.blendType = LBT_COLOUR;
				texture[i].color_blend_mode.operation = LBX_MODULATE;
				texture[i].color_blend_mode.source1 = LBS_TEXTURE;
				texture[i].color_blend_mode.source2 = LBS_CURRENT;
				texture[i].color_blend_mode.colorArg1 = Color(0,0,0,0);
				texture[i].color_blend_mode.colorArg2 = Color(0,0,0,0);
				texture[i].color_blend_mode.alphaArg1 = Color(0,0,0,0);
				texture[i].color_blend_mode.alphaArg2 = Color(0,0,0,0);
				texture[i].color_blend_mode.factor = 0;


				texture[i].alpha_blend_mode.blendType = LBT_ALPHA;
				texture[i].alpha_blend_mode.operation = LBX_MODULATE;
				texture[i].alpha_blend_mode.source1 = LBS_TEXTURE;
				texture[i].alpha_blend_mode.source2 = LBS_CURRENT;
				texture[i].alpha_blend_mode.colorArg1 = Color(0,0,0,0);
				texture[i].alpha_blend_mode.colorArg2 = Color(0,0,0,0);
				texture[i].alpha_blend_mode.alphaArg1 = Color(0,0,0,0);
				texture[i].alpha_blend_mode.alphaArg2 = Color(0,0,0,0);
				texture[i].alpha_blend_mode.factor = 0;


				texture[i].transform = Matrix4<REAL>::IDENTITY;
				texture[i].tex_transform_coord_dim = TCD_DISABLE;

				texture[i].tex_coord_calc = TEXCALC_NONE;
				texture[i].tex_coord_calc_index = 0;
			}
		}
	};
	/**
	*   Manage render state assignments.
	*/
	class CGStateManager: public Singleton<CGStateManager>
	{
	public:
		typedef std::map<CGpass,CGPassData> PassDataMap;

		CGStateManager();

		~CGStateManager();

		CGcontext GetContext(){return m_cgcontext;}

		CGPassData& GetPassData(CGpass pass);

		friend CGbool SetStateAssignment(CGstateassignment as);
		friend CGbool ResetStateAssignment(CGstateassignment as);
		friend CGbool ValidateStateAssignment(CGstateassignment as);

	private:

		PassDataMap m_passData;

		CGcontext m_cgcontext;
	};

}

#endif
